<template>
    <div class="virtual-content" ref="scrollDom" :style="{ height: height + 'px' }">
        <div class="virtual-list" :style="{ height: ((itemHeight + margin) * count) + 'px' }">
            <slot :topIndex="top" :bottomIndex="bottom"></slot>
        </div>
    </div>
</template>

<script>
import { debounce } from 'lodash-es'
import { defineComponent, ref, onMounted, onUnmounted } from 'vue'
export default defineComponent({
  name: 'VirtualComponent',
  props: {
    // offset指个数，上下偏移多少个开始渲染
    offset: {
      type: Number,
      default: 4
    },
    // 总数量
    count: {
      required: true,
      type: Number,
      default: 0
    },
    // 间距
    margin: {
      type: Number,
      default: 0
    },
    // 总高度
    height: {
      required: true,
      type: Number,
      default: 0
    },
    // 单项高度
    itemHeight: {
      required: true,
      type: Number,
      default: 0
    }
  },
  setup (props, ctx) {
    // props解构后没有响应式
    const top = ref(0)
    const bottom = ref(0)
    const firstBottom = ref(0)
    const scrollDom = ref(null)
    const initButtomIndex = () => {
      const index = props.height / (props.margin + props.itemHeight)
      const bottomIndex = Math.ceil(index)
      bottom.value = bottomIndex + props.offset
      firstBottom.value = bottomIndex + props.offset
    }
    initButtomIndex()
    const scrollEvent = (e) => {
      let scrollTop = e.target.scrollTop
      let scrollItem = props.itemHeight + props.margin
      if (scrollTop >= scrollItem) {
        let scrollIndex = Math.ceil(scrollTop / scrollItem)
        let offset = props.offset
        top.value = scrollIndex >= offset ? scrollIndex - offset : 0
        bottom.value = scrollIndex + firstBottom.value
        scrollIndex = null
        offset = null
      } else {
        top.value = 0
        bottom.value = firstBottom.value
      }
      scrollTop = null
      scrollItem = null
    }
    let debounceScroll = debounce(scrollEvent, 16, {
      trailing: true
    })
    // 监听滚动
    const initScroll = () => {
      scrollDom.value.addEventListener('scroll', debounceScroll, {
        passive: true
      })
    }
    onMounted(() => {
      initScroll()
    })
    onUnmounted(() => {
      if (scrollDom.value && scrollDom.value) {
        scrollDom.value.removeEventListener('scroll', debounceScroll, {
          passive: true
        })
        debounceScroll = null
      }
    })
    return {
      top, // 渲染起点index
      bottom, // 渲染结尾index
      firstBottom, // 渲染初使长度
      scrollDom
    }
  }
})
</script>

<style lang="less" scoped>
    .virtual-content {
        width: 100%;
        height: 100%;
        overflow-y: auto;
        .virtual-list {
            position: relative;
            /deep/ .virtual-scroll-item {
                position: absolute;
            }
        }
    }
</style>
